# -*- coding: utf-8 -*-

'''
Created on 26/11/2011

@author: Luis M. Ugalde
www.forondarena.net

Copyright © 2011 Luis M. Ugalde
Licensed under the terms of the MIT License
(see LICENSE file for details)

This software can't be considered to be even in Alpha state. It still needs a lot of work.
'''


from . import abstractparser

import re
import zmq
import pickle
import uuid
from parsers.parserexception import ParserException


class ParserQmail(abstractparser.AbstractParser):
    '''
    Class to parse Qmail log files.
    This parser does not handle antispam/antivirus or any other add-on.
    This parser should be extended with a per-site regular expression group.
    The date format can be decoded with utilities such as tai64nlocal.
    '''


    def __init__(self, helperfilename = None):
        '''
        Define the available regular expressions for the parser.
        The helper filename is used to record unparseable lines. This will allow the sysadmin to review
        and improve the ruleset.
        '''
        
        print self.__class__.__name__ + ": ParserQmail instance created"

        _beginregexp = '^(?P<date>@\w+)\s+'
        
        self.nologstatus =   re.compile(_beginregexp + 'status:\s+local\s+\S+\s+remote\s+\S+$')

        self.newconnregexp = re.compile(_beginregexp + 'new\s+msg\s+(?P<queueid>\d+)$')
        self.fromregexp =    re.compile(_beginregexp + 'info\s+msg\s+(?P<queueid>\d+):\s+bytes\s(?P<size>\d+)\s+from\s+<(?P<from>\S*)>\s+.*$')

        self.deliverregexp = re.compile(_beginregexp + 'starting\s+delivery\s+(?P<subqueueid>\d+):\s+msg\s+(?P<queueid>\d+)\s+to\s+(local|remote)\s+(?P<to>\S+)$')

        self.statusregexp = re.compile(_beginregexp + 'delivery\s+(?P<subqueueid>\d+):\s+(deferral|failure|success):\s+(?P<status>.+)$')

        self.endregexp = re.compile(_beginregexp + 'end\s+msg\s+(?P<queueid>\d+)$')
        
        abstractparser.AbstractParser.__init__(self)
        
        self.setHelper(helperfilename)


    def consumeLine(self, line):
        usehelper = self.useHelper()
        
        if line == 'NODATA':
            tmpbuffer = ['NODATA', 'NODATA', '', {}]
            pickledbuffer = pickle.dumps(tmpbuffer)
            self.parserbindsocket.send(pickledbuffer)
            print "{}-{}: Sent NODATA signal.".format(self.__class__.__name__, self.parserbindaddress)
            return
        
        matchnologstatus = self.nologstatus.match(line)
        if matchnologstatus: return

        matchnewconn = self.newconnregexp.match(line)
        if matchnewconn:
            linebits = matchnewconn.groupdict()
            tmpbuffer = ['KEYDATA', 'NEWID', linebits['queueid'], {'datein': linebits['date']}]
            pickledbuffer = pickle.dumps(tmpbuffer)
            self.parserbindsocket.send(pickledbuffer)
            if __debug__:
                print "{}-{}: Sent KEYDATA, NEWID signal.".format(self.__class__.__name__, self.parserbindaddress)
            return

        matchfrom = self.fromregexp.match(line)
        if matchfrom:
            linebits = matchfrom.groupdict()
            tmpbuffer = ['KEYDATA', 'DATA', linebits['queueid'], 
                         {'mailfrom': linebits['from'] if linebits['from'] else "MAILER-DAEMON", 'size': linebits['size']}]
            pickledbuffer = pickle.dumps(tmpbuffer)
            self.parserbindsocket.send(pickledbuffer)
            if __debug__:
                print "{}-{}: Sent KEYDATA, mail from signal.".format(self.__class__.__name__, self.parserbindaddress)
            return

        matchdeliver = self.deliverregexp.match(line)
        if matchdeliver:
            linebits = matchdeliver.groupdict()
            tmpbuffer = ['SUBKEYDATA', 'NEWID', linebits['queueid'], {'dateout': linebits['date'], 'subqueueid': linebits['subqueueid'], 'rcptto': linebits['to']}]
            pickledbuffer = pickle.dumps(tmpbuffer)
            self.parserbindsocket.send(pickledbuffer)
            if __debug__:
                print "{}-{}: Sent SUBKEYDATA, NEWID signal.".format(self.__class__.__name__, self.parserbindaddress)
            return

        matchstatus = self.statusregexp.match(line)
        if matchstatus:
            linebits = matchstatus.groupdict()
            tmpbuffer = ['SUBKEYDATA', 'DATA', linebits['subqueueid'], {'dateout': linebits['date'], 'status': linebits['status']}]
            pickledbuffer = pickle.dumps(tmpbuffer)
            self.parserbindsocket.send(pickledbuffer)
            if __debug__:
                print "{}-{}: Sent SUBKEYDATA, DATA signal.".format(self.__class__.__name__, self.parserbindaddress)
            #   I also end the subqueueid here, as I want the state store to be generic, and because qmail doesn't bother to log when subqueueid ends,
            # which, by the way, is at every attempt.
            tmpbuffer = ['SUBKEYDATA', 'ENDID', linebits['subqueueid'], {'dateout': linebits['date']}]
            pickledbuffer = pickle.dumps(tmpbuffer)
            self.parserbindsocket.send(pickledbuffer)
            if __debug__:
                print "{}-{}: Sent SUBKEYDATA, ENDID signal.".format(self.__class__.__name__, self.parserbindaddress)
            return

        matchend = self.endregexp.match(line)
        if matchend:
            linebits = matchend.groupdict()
            tmpbuffer = ['KEYDATA', 'ENDID', linebits['queueid'], {'dateend': linebits['date']}]
            pickledbuffer = pickle.dumps(tmpbuffer)
            self.parserbindsocket.send(pickledbuffer)
            if __debug__:
                print "{}-{}: Sent KEYDATA Message END signal.".format(self.__class__.__name__, self.parserbindaddress)
            return
        
        if usehelper:
            self.writeHelper(line + "\n")
            if __debug__:
                print "{}-{}: Unparseable line. Writing to helper file: {}".format(self.__class__.__name__, self.parserbindaddress, self.filename)


    def parseLines(self):
        '''
        Process a line previously read from a reader.
        Create a message with the parsed tokens, and send it to the state store.
        '''
        
        mustexit = 0
        idlesockets = 0

        while True:
            socks = dict(self.poller.poll(3000))
            if socks:
                idlesockets = 0
                for i in self.readerconnectsockets:
                    if i in socks and socks.get(i) == zmq.POLLIN:
                        line = i.recv()
                        if __debug__:
                            print "{}-{}: The line is {}.".format(self.__class__.__name__, self.parserbindaddress, line)
                        self.consumeLine(line)
                if self.readerconnectnotifysocket in socks and socks.get(self.readerconnectnotifysocket) == zmq.POLLIN:
                    line = self.readerconnectnotifysocket.recv()
                    if __debug__:
                        print "{}-{}: The line is {}.".format(self.__class__.__name__, self.parserbindnotifyaddress, line)
                    if line == 'END':
                        print self.__class__.__name__ + ": Received END signal. Setting END flag."
                        mustexit = 1
            else:
                if mustexit > 0:
                    print "{}-{}: No data left at parser for 3 seconds with end signal. Ending.".format(self.__class__.__name__, self.parserbindaddress)
                    self.parserbindnotifysocket.send('END')
                    print "{}-{}: Sent END signal to {}.".format(self.__class__.__name__, self.parserbindaddress, self.parserbindnotifyaddress)
                    break
                if idlesockets > 3:
                    print "{}-{}: Too long with no activity from sockets. Should probably do something. TODO.".format(self.__class__.__name__, self.parserbindaddress)
                    idlesockets = 0
                idlesockets += 1

        self.closeSources()
        self.closeNotifySources()
        self.closeBindParser()
        self.closeBindNotifyParser()
        self.closeContext()
        self.closeHelper()


if __name__ == '__main__':
    print "Please, do not run this"
